package com.nulldev.util.VariableAPI;

import java.nio.ByteBuffer;
import java.util.Random;
import java.util.UUID;

import com.nulldev.util.VariableAPI.util.random.GlobalRandom;

public class RandomUtil {

	public static int randInt(final int min, final int max) {
		return randInt(min, max, GlobalRandom.getGlobalInsecureRandom());
	}

	public static int randInt(final int min, final int max, final Random rand) {
		if (min == 0 && max >= 0) {
			return rand.nextInt(max + 1);
		} else {
			return nextIntInRange(min, max, rand);
		}
	}

	public static int nextIntInRange(final int min, final int max, final Random rng) {
		if (min == max) {
			return min;
		} else if (min > max) {
			throw new IllegalArgumentException("Cannot draw random int from invalid range [" + min + ", " + max + "].");
		}
		final int diff = max - min;
		if (diff >= 0 && diff != Integer.MAX_VALUE) {
			return (min + rng.nextInt(diff + 1));
		}
		int i;
		do {
			i = rng.nextInt();
		} while (i < min || i > max);
		return i;
	}

	public static float randFloat(final float min, final float max) {
		return randFloat(min, max, GlobalRandom.getGlobalInsecureRandom());
	}

	public static float randFloat(final float minInclusive, final float maxInclusive, final Random rand) {
		return randFloat(minInclusive, maxInclusive, 0.0001d, rand);
	}

	public static float randFloat(final float minInclusive, final float maxInclusive, final double precision, final Random rand) {
		final int max = (int) (maxInclusive / precision);
		final int min = (int) (minInclusive / precision);
		final int randomInt = rand.nextInt((max - min) + 1) + min;
		final float randomNum = (float) (randomInt * precision);
		return randomNum;
	}

	public static long randLong(final long min, final long max) {
		return randLong(min, max, GlobalRandom.getGlobalInsecureRandom());
	}

	public static long randLong(final long min, final long max, final Random rand) {
		return raw_nextLong(rand, min, max);
	}

	private static long raw_nextLong(final Random r, final long min, final long max) {
		if (min > max)
			throw new IllegalArgumentException("min>max");
		if (min == max)
			return min;
		long n = r.nextLong();
		n = n == Long.MIN_VALUE ? 0 : n < 0 ? -n : n;
		n = n % (max - min);
		return min + n;
	}

	public static String hexString(final int chars) {
		return hexString(chars, GlobalRandom.getGlobalInsecureRandom());
	}

	public static String hexString(final int chars, final Random rand) {
		final StringBuffer sb = new StringBuffer();
		while (sb.length() < chars) {
			sb.append(Integer.toHexString(rand.nextInt()));
		}
		return sb.toString().substring(0, chars);
	}

	public static UUID quickUUID() {
		return quickUUID(GlobalRandom.getGlobalInsecureRandom());
	}

	public static UUID quickUUID(final Random rand) {
		final byte[] randomBytes = new byte[16];
		rand.nextBytes(randomBytes);
		randomBytes[6] &= 0x0f; /* clear version */
		randomBytes[6] |= 0x40; /* set to version 4 */
		randomBytes[8] &= 0x3f; /* clear variant */
		randomBytes[8] |= 0x80; /* set to IETF variant */
		return UUID.nameUUIDFromBytes(randomBytes);
	}

	public static boolean randomBoolean() {
		return randomBoolean(GlobalRandom.getGlobalInsecureRandom());
	}

	public static boolean randomBoolean(final Random rand) {
		return rand.nextDouble() < 0.5;
	}

	public static double random() {
		return random(GlobalRandom.getGlobalInsecureRandom());
	}

	public static double random(final Random rand) {
		return rand.nextDouble();
	}

	public static void bytes(final byte[] bytes) {
		bytes(bytes, GlobalRandom.getGlobalInsecureRandom());
	}

	public static void bytes(final byte[] bytes, final Random rand) {
		rand.nextBytes(bytes);
	}

	public static byte[] bytes(final int count) {
		return bytes(count, GlobalRandom.getGlobalInsecureRandom());
	}

	public static byte[] bytes(final int count, final Random rand) {
		if (count <= 0)
			throw new UnsupportedOperationException("bytes <= 0!");
		final byte[] bytes = new byte[count];
		rand.nextBytes(bytes);
		return bytes;
	}

	public static long randomLong() {
		return randomLong(GlobalRandom.getGlobalInsecureRandom());
	}

	public static long randomLong(final Random rand) {
		return rand.nextLong();
	}

	public static int randomInt() {
		return randomInt(GlobalRandom.getGlobalInsecureRandom());
	}

	public static int randomIntPositive() {
		return randomIntPositive(GlobalRandom.getGlobalInsecureRandom());
	}

	public static int randomInt(final Random rand) {
		return rand.nextInt();
	}

	public static int randomIntPositive(final Random rand) {
		return rand.nextInt() & Integer.MAX_VALUE;
	}

	public static ByteBuffer buffer(final int count) {
		return buffer(count, GlobalRandom.getGlobalInsecureRandom());
	}

	public static ByteBuffer buffer(final int count, final Random rand) {
		if (count <= 0)
			throw new UnsupportedOperationException("count <= 0!");
		final byte[] bytes = new byte[count];
		rand.nextBytes(bytes);
		return ByteBuffer.wrap(bytes);
	}
}
